﻿using System.Runtime.InteropServices;
using System.Windows;
using RPGOnlineWPF.Renderer;
using SharpDX;
using SharpDX.D3DCompiler;
using SharpDX.Direct3D;
using SharpDX.Direct3D10;
using SharpDX.DXGI;
using Device = SharpDX.Direct3D10.Device;

namespace RPGOnlineWPF.Model.MapEditor
{
	public class Grid : BaseInteractive<D3D10>
	{
		private Effect m_simpleEffect;
		private InputLayout m_vertexLayout;
		private DataStream m_vertexStream;
		private Buffer m_vertices;
		private int m_linesCount;
		private D3D10 m_renderer;

		public float MapWidth { get; set; }
		public float MapHeight { get; set; }
		public Color4 GridColor { get; set; }

		public Grid(float mapWidth, float mapHeight, Color4 gridColor)
		{
			MapWidth = mapWidth;
			MapHeight = mapHeight;
			GridColor = gridColor;
		}

		public override void RenderScene(DrawEventArgs args)
		{
			Device device = m_renderer.Device;
			if (device == null)
				return;

			device.InputAssembler.InputLayout = m_vertexLayout;
			device.InputAssembler.PrimitiveTopology = PrimitiveTopology.LineList;
			device.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(m_vertices, Marshal.SizeOf(typeof(Vector4)), 0));

			EffectTechnique technique = m_simpleEffect.GetTechniqueByIndex(0);
			EffectPass pass = technique.GetPassByIndex(0);

			var color = m_simpleEffect.GetVariableBySemantic("Color").AsVector();
			color.Set(GridColor);

			var modelViewProjection = m_renderer.Camera.View * m_renderer.Camera.Projection;
			var matrix = m_simpleEffect.GetVariableBySemantic("ModelViewProjection").AsMatrix();
			matrix.SetMatrix(modelViewProjection);

			for (int i = 0; i < technique.Description.PassCount; ++i)
			{
				pass.Apply();
				device.Draw(m_linesCount * 2, 0);
			}
		}

		public override void Attach()
		{
			Device device = m_renderer.Device;
			if (device == null)
				return;

			var compilationResult = ShaderBytecode.CompileFromFile("Resources\\Grid.fx", "fx_4_0");
			if (compilationResult.HasErrors)
				MessageBox.Show(compilationResult.Message);

			ShaderBytecode shaderBytes = compilationResult;
			m_simpleEffect = new Effect(device, shaderBytes);

			EffectTechnique technique = m_simpleEffect.GetTechniqueByIndex(0);
			EffectPass pass = technique.GetPassByIndex(0);

			m_vertexLayout = new InputLayout(device, pass.Description.Signature, new[] {
                new InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0)
            });

			int verticalLines = (int)(MapWidth / 1.5f) + 1;
            int horizontalLines = (int)(MapHeight / 1.5f) + 1;

			int index = 0;
			var vertices = new Vector4[2*verticalLines + 2*horizontalLines];
			for (int x = 0; x < verticalLines; x++)
			{
				vertices[index++] = new Vector4(x * 1.5f, 0, 0, 1.0f);
				vertices[index++] = new Vector4(x * 1.5f, MapHeight, 0, 1.0f);
			}

			for (int y = 0; y < horizontalLines; y++)
			{
				vertices[index++] = new Vector4(0, y * 1.5f, 0, 1.0f);
				vertices[index++] = new Vector4(MapWidth, y * 1.5f, 0, 1.0f);
			}

			m_linesCount = verticalLines + horizontalLines;

			m_vertexStream = new DataStream(vertices.Length * Marshal.SizeOf(typeof(Vector4)), true, true);
			m_vertexStream.WriteRange(vertices);
			m_vertexStream.Position = 0;

			m_vertices = new Buffer(device, m_vertexStream, new BufferDescription()
			{
				BindFlags = BindFlags.VertexBuffer,
				CpuAccessFlags = CpuAccessFlags.None,
				OptionFlags = ResourceOptionFlags.None,
				SizeInBytes = (int) m_vertexStream.Length,
				Usage = ResourceUsage.Default
			});

			device.Flush();
		}

		public override void Detach()
		{
			m_vertices.Dispose();
			m_vertexLayout.Dispose();
			m_simpleEffect.Dispose();
			m_vertexStream.Dispose();
		}

		public override D3D10 Renderer
		{
			get { return m_renderer; }
			set { m_renderer = value; }
		}

		public override void Reset(DrawEventArgs args)
		{
		}

		public override void Render(DrawEventArgs args)
		{
		}
	}
}